/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib.tests;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.HashMap;
import java.util.Map;

import org.antlr.runtime.RecognitionException;
import org.junit.Before;
import org.junit.Test;

import ac.jp.u_tokyo.SyncLib.Mod;
import ac.jp.u_tokyo.SyncLib.NullMod;
import ac.jp.u_tokyo.SyncLib.PrimMod;
import ac.jp.u_tokyo.SyncLib.SynchronizationFailedException;
import ac.jp.u_tokyo.SyncLib.dictionaries.DictMod;
import ac.jp.u_tokyo.SyncLib.dictionaries.DictModFactory;
import ac.jp.u_tokyo.SyncLib.dictionaries.DynamicGet;
import ac.jp.u_tokyo.SyncLib.modParser.ModParser;

public class DynamicGetTest extends GetTestBase
{
	private DynamicGet<String> _sync;

	private DynamicGet<String> _changeKeySync;

	@Before
	public void setUp() throws SynchronizationFailedException
	{
//		final HashMap<Integer, ForMap<String, Object>> initialModules = new HashMap<Integer, ForMap<String, Object>>();
//		initialModules.put(1, new HashMap<String, Object>());

		_sync = new DynamicGet(true, false);
		_sync.resynchronize(new Object[] { "a", new HashMap<String, Object>(), null}, new Mod[] {NullMod.INSTANCE, NullMod.INSTANCE, NullMod.INSTANCE});
		_changeKeySync = new DynamicGet<String>(true, true);
		_changeKeySync.resynchronize(new Object[] { "a", new HashMap<String, Object>(), null}, new Mod[] {NullMod.INSTANCE, NullMod.INSTANCE, NullMod.INSTANCE});
	}

	@Test
	public void testStability() throws SynchronizationFailedException
	{
		final Mod[] nulls = new Mod[] { NullMod.INSTANCE, NullMod.INSTANCE,
				NullMod.INSTANCE };
		Mod[] result = checkedSynchronize(_sync, nulls);
		assertArrayEquals(nulls, result);

		result = checkedSynchronize(_changeKeySync, nulls);
		assertArrayEquals(nulls, result);

	}

	@Test
	public void testStability2() throws SynchronizationFailedException,
			RecognitionException
	{
		final HashMap<Integer, Map<String, Object>> initialModules = new HashMap<Integer, Map<String, Object>>();
		initialModules.put(1, new HashMap<String, Object>());

		_sync = new DynamicGet(true, false);
		_sync.resynchronize(new Object[] { 1, initialModules, new HashMap<String, Object>()}, new Mod[] {NullMod.INSTANCE, NullMod.INSTANCE, NullMod.INSTANCE});
		final Mod[] nulls = new Mod[] { NullMod.INSTANCE, NullMod.INSTANCE,
				NullMod.INSTANCE };
		_sync.setRevertPoint();
		checkedResynchronize(_sync, new Object[] { 1, initialModules,
				initialModules.get(1) }, ModParser
				.parse("3,{3->{Name->module}},nomod"));
		_sync.cancelRevertPoint();

		_sync.setRevertPoint();
		Mod[] result = checkedSynchronize(_sync, nulls);
		assertArrayEquals(nulls, result);

	}

	@Test
	public void testMatch() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_changeKeySync, new Mod[] {
				new PrimMod<String>("a"), abcMod, NullMod.INSTANCE });
		Mod[] result = checkedSynchronize(_changeKeySync, new Mod[] {
				NullMod.INSTANCE, NullMod.INSTANCE, new PrimMod<Integer>(200) });

		DictModFactory r = new DictModFactory<String, Integer>();
		r.addPut("b", new PrimMod<Integer>(200));

		assertEquals(new PrimMod<String>("b"), result[0]);
		assertFalse(r.create().isConflict(result[1]));
		assertEquals(new PrimMod<Integer>(200), result[2]);
	}

	@Test
	public void testMatchMod() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_changeKeySync, new Mod[] {
				new PrimMod<String>("a"), abcMod, NullMod.INSTANCE });
		Mod[] result = checkedSynchronize(_changeKeySync, new Mod[] {
				NullMod.INSTANCE, NullMod.INSTANCE, new PrimMod<Integer>(400) });

		DictModFactory r = new DictModFactory<String, Integer>();
		r.addPut("a", new PrimMod<Integer>(400));

		assertTrue(new PrimMod<String>("a").equals(result[0])
				|| result[0] instanceof NullMod);
		assertFalse(r.create().isConflict(result[1]));
		assertEquals(new PrimMod<Integer>(400), result[2]);
	}

	@Test
	public void testStatePreservation() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_sync, new Mod[] { new PrimMod<String>("a"), abcMod,
				NullMod.INSTANCE });

		try
		{
			checkedSynchronize(_sync, new Mod[] { new PrimMod<String>("b"),
					createABCMod2(), new PrimMod<Integer>(1000) });
		}
		catch (SynchronizationFailedException e)
		{
			Mod[] result = checkedSynchronize(_sync, new Mod[] {
					new PrimMod<String>("b"), NullMod.INSTANCE,
					NullMod.INSTANCE });

			assertEquals(new PrimMod<String>("b"), result[0]);
			assertEquals(new PrimMod<Integer>(200), result[2]);
			return;

		}
		fail();
	}

	@Test
	public void testStatePreservationForResync()
			throws SynchronizationFailedException, RecognitionException
	{
		checkedSynchronize(_sync, ModParser
				.parse("a, {a->100, b->200, c->300}, nomod"));
		_sync.setRevertPoint();
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 500);
		map.put("b", 400);
		checkedResynchronize(_sync, new Object[] { "a", map, 500 }, ModParser
				.parse("b, nomod, 600"));
		Mod[] result1 = checkedSynchronize(_sync, ModParser
				.parse("a, nomod, nomod"));
		assertEquals(ModParser.parse("500")[0], result1[2]);
		_sync.revert();
		Mod[] result2 = checkedSynchronize(_sync, ModParser
				.parse("b, nomod, nomod"));
		assertEquals(ModParser.parse("200")[0], result2[2]);
	}

	@Test
	public void testChangeKey() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_sync, new Mod[] { new PrimMod<String>("a"), abcMod,
				NullMod.INSTANCE });
		final Mod[] result = checkedSynchronize(_sync, new Mod[] {
				new PrimMod<String>("b"), NullMod.INSTANCE, NullMod.INSTANCE });

		DictModFactory r = new DictModFactory<String, Integer>();
		r.addPut("b", new PrimMod<Integer>(200));

		assertEquals(new PrimMod<String>("b"), result[0]);
		assertEquals(r.create(), result[1]);
		assertEquals(new PrimMod<Integer>(200), result[2]);
	}

	@Test
	public void testChangeKeyAndVal() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_sync, new Mod[] { new PrimMod<String>("a"), abcMod,
				NullMod.INSTANCE });
		final Mod[] result = checkedSynchronize(_sync, new Mod[] {
				new PrimMod<String>("b"), NullMod.INSTANCE,
				new PrimMod<Integer>(500) });

		DictModFactory r1 = new DictModFactory<String, Integer>();
		r1.addPut("b", new PrimMod<Integer>(500));

		assertEquals(new PrimMod<String>("b"), result[0]);
		assertEquals(r1.create(), result[1]);
		assertEquals(new PrimMod<Integer>(500), result[2]);

	}

	@Test(expected = SynchronizationFailedException.class)
	public void testNullKey() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_sync, new Mod[] { PrimMod.NULL, abcMod,
				NullMod.INSTANCE });
	}

	@Test
	public void testRemoveKey() throws SynchronizationFailedException
	{
		DictMod<String, Integer> abcMod = creatABCMod();
		checkedSynchronize(_sync, new Mod[] { new PrimMod<String>("a"), abcMod,
				NullMod.INSTANCE });

		DictModFactory<String, Integer> f2 = new DictModFactory<String, Integer>();
		f2.addRemove("c");

		Mod[] result = checkedSynchronize(_sync, new Mod[] { NullMod.INSTANCE,
				f2.create(), NullMod.INSTANCE });

		assertTrue(new PrimMod<String>("a").equals(result[0])
				|| result[0] instanceof NullMod);
		assertEquals(f2.create(), result[1]);
		assertEquals(NullMod.INSTANCE, result[2]);

	}

	private DictMod<String, Integer> creatABCMod()
	{
		DictModFactory<String, Integer> f = new DictModFactory<String, Integer>();
		f.addPut("a", new PrimMod<Integer>(100));
		f.addPut("b", new PrimMod<Integer>(200));
		f.addPut("c", new PrimMod<Integer>(300));
		DictMod<String, Integer> abcMod = f.create();
		return abcMod;
	}

	private DictMod<String, Integer> createABCMod2()
	{
		DictModFactory<String, Integer> f = new DictModFactory<String, Integer>();
		f.addPut("a", new PrimMod<Integer>(500));
		f.addPut("b", new PrimMod<Integer>(600));
		f.addPut("c", new PrimMod<Integer>(700));
		DictMod<String, Integer> abcMod = f.create();
		return abcMod;
	}

	@Override
	protected void setDictOverVal(boolean dictOverVal)
	{
		_sync.setDictOverVal(dictOverVal);

	}

	@Override
	protected Mod[] internalResynchronize(Map<String, Integer> map, Integer v,
			Mod dictMod, Mod<Integer> mod)
			throws SynchronizationFailedException
	{
		Mod[] result = _sync.resynchronize(new Object[] { KEY, map, v },
				new Mod[] { new PrimMod(KEY), dictMod, mod });
		return new Mod[] { result[1], result[2] };
	}

	@Override
	protected Mod[] internalSynchronize(Mod dictMod, Mod<Integer> mod)
			throws SynchronizationFailedException
	{
		Mod[] result = _sync.synchronize(new Mod[] { new PrimMod(KEY), dictMod,
				mod });
		return new Mod[] { result[1], result[2] };
	}

}
